#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "tjpgd.h"
#include "lodepng.h"

#define DEFFAULT_DEVICE						"/dev/lcd0"
#define BUFF_SIZE							64
#define US_AMOUNT_OF_DATA					28	/* Unsigned short amount of data in one package. */
#define IMG_WIDTH_MAX						320
#define IMG_HEIGHT_MAX						240
#define GREEN_MASK							0x07E0
#define BLUE_MASK							0xF800
#define CONVERT_888RGB_TO_565BGR(r, g, b)	(((unsigned short)r >> 3) | (((unsigned short)g <<3 ) & GREEN_MASK) | (((unsigned short)b << 8) & BLUE_MASK))
#define IMAGE_STEP							960
#define IMAGE_CHANNELS						3

#define CARD_ADDR							0x00

#define CARD_START_BITMAP_TYPE_DATA			0x01
#define CARD_SEQUEL_BITMAP_TYPE_DATA		0x02
#define CARD_LEDS_STATE_TYPE_DATA	0X03
#define CARD_JPEG_TYPE_DATA					0x04

#define CARD_LED_NUM						0x01
#define CARD_LED_ON							0x01
#define CARD_LED_OFF						0x00

#define CARD_JPEG_STATE_START_PICTURE		0x00
#define CARD_JPEG_STATE_BREAK_PICTURE		0x01
#define CARD_JPEG_STATE_END_PICTURE			0X02

typedef struct
{
	unsigned short start_x;
	unsigned short start_y;
	unsigned short end_x;
	unsigned short end_y;
}CARD_StartBitmapTypeData;

typedef struct
{
	unsigned short data_count;
	unsigned short data[];
}CARD_SequelBitmapTypeData;

typedef struct
{
	unsigned char addr;
	unsigned char type;
	unsigned char data[];
}CARD_TypeProtocol;

typedef struct
{
	unsigned char num_of_leds;
	unsigned char state;
}CARD_LedsState;

typedef struct
{
	unsigned short buff_size;
	unsigned char state;
	unsigned char data[];
}CARD_JpegTypeData;

typedef struct
{
	FILE *fp;
	char *file_name;
	unsigned short **fbuf;
	CARD_TypeProtocol *card_type_protocol;
}IODEV;

typedef enum
{
	RETVAL_OK,
	RETVAL_ERROR,
	RETVAL_BAD_COMMAND,
	RETVAL_UNKNOWN_COMMAND,
	RETVAL_UNDEFINED_STATUS
}RETVAL_STATE;


RETVAL_STATE AppLedState(CARD_TypeProtocol *card_type_protocol, int argc, char **argv);
RETVAL_STATE AppPictureStateJpg(CARD_TypeProtocol *card_type_protocol, int argc, char **argv);
RETVAL_STATE AppPictureStatePng(CARD_TypeProtocol *card_type_protocol, int argc, char **argv);
RETVAL_STATE AppSequelData(IODEV *devid, JDEC *jdec);
RETVAL_STATE AppSendBuffer(char *data, unsigned short size);

UINT in_func(JDEC *jd, BYTE *buff, UINT nbyte);
UINT out_func(JDEC *jd, void *bitmap, JRECT *rect);

RETVAL_STATE AppLedState(CARD_TypeProtocol *card_type_protocol, int argc, char **argv)
{
	CARD_LedsState *card_leds_state = (void *)(card_type_protocol->data);
	RETVAL_STATE retval = RETVAL_OK;
	unsigned short size;

	card_type_protocol->addr = CARD_ADDR;
	card_type_protocol->type = CARD_LEDS_STATE_TYPE_DATA;
	card_leds_state->num_of_leds = CARD_LED_NUM;

	if(argc < 3)
	{
		printf("Undefined status Led!\n");
		retval = RETVAL_UNDEFINED_STATUS;
	}
	else
	{
		if(!strcmp(argv[2], "on"))
		{
			card_leds_state->state = CARD_LED_ON;
			printf("Led on!\n");
		}
		else if(!strcmp(argv[2], "off"))
		{
			card_leds_state->state = CARD_LED_OFF;
			printf("Led off!\n");
		}
		else
		{
			printf("Unknown command!\n");
			retval = RETVAL_UNKNOWN_COMMAND;
		}
	}

	if(!retval)
	{
		size = sizeof(CARD_TypeProtocol) + sizeof(CARD_LedsState);
		retval = AppSendBuffer((void *)card_type_protocol, size);
	}

	return retval;
}

RETVAL_STATE AppPictureStateJpg(CARD_TypeProtocol *card_type_protocol, int argc, char **argv)
{
	IODEV iodev;
	void *work_buffer;
	JDEC jdec;
	JRESULT result;
	RETVAL_STATE retval = RETVAL_OK;
	unsigned short count;

	if(argc < 3)
	{
		printf("Enter the name of the image!\n");
		retval = RETVAL_UNDEFINED_STATUS;
	}
	else
	{
		iodev.card_type_protocol = (void *)card_type_protocol;
		iodev.file_name = argv[2];

		iodev.fp = fopen(iodev.file_name, "rb");
		if(!iodev.fp)
		{
			printf("Faild open file name: %s!\n", iodev.file_name);
			retval = RETVAL_ERROR;
		}
		else
		{
			work_buffer = malloc(3100);

			result = jd_prepare(&jdec, in_func, work_buffer, 3100, &iodev);
			if(result == JDR_OK)
			{
				printf("Image dimensions: %u by %u.\n", jdec.width, jdec.height);

				if(jdec.width <= IMG_WIDTH_MAX && jdec.height <= IMG_HEIGHT_MAX)
				{
					/* Necessarily rewrite one-dimensional array! */
					iodev.fbuf = (unsigned short **)calloc(jdec.height, sizeof(unsigned short *));
					if(iodev.fbuf == NULL)
					{
						printf("Not created a dynamic array.\n");
					}
					for(count = 0; count < jdec.height; count++)
					{
						iodev.fbuf[count] = (unsigned short *)calloc(jdec.width, sizeof(unsigned short));
						if(iodev.fbuf[count] == NULL)
						{
							printf("Not created a dynamic array.\n");
						}
					}

					result = jd_decomp(&jdec, out_func, 0);
					if(result == JDR_OK)
					{
						printf("Decompression succeeded Ok.\n");
						retval = AppSequelData(&iodev, &jdec);
					}
					else
					{
						printf("Failed to decompress: rc = %d.\n", result);
						retval = RETVAL_ERROR;
					}

					for(count = 0; count < jdec.height; count++)
					{
						free(iodev.fbuf[count]);
					}
					free(iodev.fbuf);
				}
				else
				{
					printf("Image size exceeds the maximum allowable.\n");
				}
			}
			else
			{
				printf("Failed to prepare: rc = %d.\n", result);
				retval = RETVAL_ERROR;
			}

			free(work_buffer);
			fclose(iodev.fp);
		}
	}

	return retval;
}
RETVAL_STATE AppSequelData(IODEV *devid, JDEC *jdec)
{

	CARD_TypeProtocol *card_type_protocol = (void *)(devid->card_type_protocol);
	CARD_StartBitmapTypeData *card_start_bitmap;
	CARD_SequelBitmapTypeData *card_sequel_bitmap;

	unsigned short size;
	RETVAL_STATE retval = RETVAL_OK;
	unsigned short i, j, threshold_of_data;

	char *dev = DEFFAULT_DEVICE;
	int fd;
	int state_write;

	fd = open(dev, O_WRONLY);
	if(fd == -1)
	{
		printf("Unable to open driver %s", dev);
		retval = RETVAL_ERROR;
	}
	else
	{

		/* Send start area bitamp. */
		card_type_protocol->addr = CARD_ADDR;
		card_type_protocol->type = CARD_START_BITMAP_TYPE_DATA;
		card_start_bitmap = (void *)(card_type_protocol->data);
		card_start_bitmap->start_x = le16toh(0);
		card_start_bitmap->end_x = le16toh(jdec->height);
		card_start_bitmap->start_y = le16toh(0);
		card_start_bitmap->end_y = le16toh(jdec->width);

		size = sizeof(CARD_TypeProtocol) + sizeof(CARD_StartBitmapTypeData);

		state_write = write(fd, (void *)card_type_protocol, size);
		if(state_write < 0)
		{
			printf("Can not write data to %s!\n", dev);
			retval = RETVAL_ERROR;
		}

		/* Send sequel bitmap. */
		card_type_protocol->addr = CARD_ADDR;
		card_type_protocol->type = CARD_SEQUEL_BITMAP_TYPE_DATA;
		card_sequel_bitmap = (void *)(card_type_protocol->data);

		size = sizeof(CARD_TypeProtocol) + sizeof(CARD_SequelBitmapTypeData);

		/* Be sure to rewrite this code! */
		for(i = 0, threshold_of_data = 0; i < jdec->height; i++)
		{
			for(j = 0; j < jdec->width; j++)
			{
				*(card_sequel_bitmap->data + threshold_of_data++) = devid->fbuf[i][j];
				if(threshold_of_data > US_AMOUNT_OF_DATA)
				{
					card_sequel_bitmap->data_count = le16toh(threshold_of_data);
					state_write = write(fd, (void *)card_type_protocol, size + (2 * threshold_of_data));
					threshold_of_data = 0;
				}
			}
		}

		close(fd);
	}

	return retval;
}

UINT in_func(JDEC *jd, BYTE *buff, UINT nbyte)
{
	IODEV *dev = (void *)(jd->device);

	if(buff)
	{
		return (UINT)fread(buff, 1, nbyte, dev->fp);
	}
	else
	{
		return (UINT)fseek(dev->fp, nbyte, SEEK_CUR) ? 0 :nbyte;
	}
}

UINT out_func(JDEC *jd, void *bitmap, JRECT *rect)
{
	IODEV *dev = (void *)(jd->device);
	unsigned short x, y, i;
	unsigned short *src;

	src = (void *)bitmap;

	for(y = rect->top, i = 0; y <= rect->bottom; y++)
	{
		for(x = rect->left; x <= rect->right; x++)
		{
			dev->fbuf[y][jd->width - x] = le16toh(src[i++]);
		}
	}

	return 1; /* Continue to decompress. */
}

RETVAL_STATE AppSendBuffer(char *data, unsigned short size)
{
	char *dev = DEFFAULT_DEVICE;
	int fd;
	int state_write;
	RETVAL_STATE retval = RETVAL_OK;

	fd = open(dev, O_WRONLY);
	if(fd == -1)
	{
		printf("Unable to open driver %s", dev);
		retval = RETVAL_ERROR;
	}
	else
	{
		state_write = write(fd, data, size);
		if(state_write < 0)
		{
			printf("Can not write data to %s!\n", dev);
			retval = RETVAL_ERROR;
		}
		else
		{
			close(fd);
		}
	}

	return retval;
}

RETVAL_STATE AppPictureStatePng(CARD_TypeProtocol *card_type_protocol, int argc, char **argv)
{
	RETVAL_STATE retval = RETVAL_OK;
	unsigned error;
	unsigned char *image;
	unsigned width, height;
	unsigned char *png;
	size_t pngsize;
	unsigned char *filename;
	CARD_StartBitmapTypeData *card_start_bitmap;
	CARD_SequelBitmapTypeData *card_sequel_bitmap;
	unsigned short size;
	unsigned short i, j, threshold_of_data, data_rgb;
	unsigned char data_b, data_g, data_r;
	char *dev = DEFFAULT_DEVICE;
	int fd;
	int state_write;

	if(argc < 3)
	{
		printf("Enter the name of the image!\n");
		retval = RETVAL_UNDEFINED_STATUS;
	}
	else
	{
		filename = argv[2];
		lodepng_load_file(&png, &pngsize, filename);
		error = lodepng_decode24(&image, &width, &height, png, pngsize);
		free(png);
		if(error)
		{
			/* printf("Error %u: %s.\n", error, lodepng_error_text(error)); */
			printf("Error %u.\n", error);
			retval = RETVAL_ERROR;
		}
		else
		{
			if(width <= IMG_WIDTH_MAX && height <= IMG_HEIGHT_MAX)
			{
				fd = open(dev, O_WRONLY);
				if(fd == -1)
				{
					printf("Unable to open driver %s", dev);
					retval = RETVAL_ERROR;
				}
				else
				{
					/* Send start area bitamp. */
					card_type_protocol->addr = CARD_ADDR;
					card_type_protocol->type = CARD_START_BITMAP_TYPE_DATA;
					card_start_bitmap = (void *)(card_type_protocol->data);
					card_start_bitmap->start_x = le16toh(0);
					card_start_bitmap->end_x = le16toh(height);
					card_start_bitmap->start_y = le16toh(0);
					card_start_bitmap->end_y = le16toh(width);

					size = sizeof(CARD_TypeProtocol) + sizeof(CARD_StartBitmapTypeData);

					state_write = write(fd, (void *)card_type_protocol, size);
					if(state_write < 0)
					{
						printf("Can not write data to %s!\n", dev);
						retval = RETVAL_ERROR;
					}

					/* Send sequel bitmap. */
					card_type_protocol->addr = CARD_ADDR;
					card_type_protocol->type = CARD_SEQUEL_BITMAP_TYPE_DATA;
					card_sequel_bitmap = (void *)(card_type_protocol->data);

					size = sizeof(CARD_TypeProtocol) + sizeof(CARD_SequelBitmapTypeData);

					/* Be sure to rewrite this code! */
					for(i = 0, threshold_of_data = 0; i < height; i++)
					{
						for(j = 0; j < width; j++)
						{
							data_b = *(image + i * IMAGE_STEP + (width - j) * IMAGE_CHANNELS);
							data_g = *(image + i * IMAGE_STEP + (width - j) * IMAGE_CHANNELS + 1);
							data_r = *(image + i * IMAGE_STEP + (width - j) * IMAGE_CHANNELS + 2);

							data_rgb = CONVERT_888RGB_TO_565BGR(data_r, data_g, data_b);

							*(card_sequel_bitmap->data + threshold_of_data++) = le16toh(data_rgb);
							if(threshold_of_data > US_AMOUNT_OF_DATA)
							{
								card_sequel_bitmap->data_count = le16toh(threshold_of_data);
								state_write = write(fd, (void *)card_type_protocol, size + (2 * threshold_of_data));
								threshold_of_data = 0;
							}
						}
					}
				close(fd);
				}
			}
			else
			{
				retval = RETVAL_ERROR;
			}
		}
		free(image);
	}
	return retval;
}

int main(int argc, char *argv[])
{
	char buffer_data[BUFF_SIZE];
	CARD_TypeProtocol *card_type_protocol = (void *)buffer_data;
	int retval = 0;

	if(argc < 2)
	{
		printf("You must enter the command: -l or -p!\n");
		exit(1);
	}

	if(!strcmp(argv[1], "-l")) /* Led command! */
	{
		printf("Command state led!\n");
		retval = AppLedState(card_type_protocol, argc, argv);
	}
	else if(!strcmp(argv[1], "-jpg")) /* Jpeg picture command! */
	{
		printf("Command state picture - jpg!\n");
		retval = AppPictureStateJpg(card_type_protocol, argc, argv);
	}
	else if(!strcmp(argv[1], "-png")) /* Png picture command! */
	{
		printf("Command state picture - png!\n");
		retval = AppPictureStatePng(card_type_protocol, argc, argv);
	}
	else
	{
		printf("Bad command!\n");
		//exit(1);
	}

	printf("End of operation.\n");

	return 0;
}

